/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "onload_callback_napi.h"
#include "imageknife_data_napi.h"

namespace ImageKnifeC {

OnLoadCallBackNapi::OnLoadCallBackNapi(napi_env env, napi_value value)
{
    env_ = env;
    napi_status status;
    napi_create_reference(env, value, 1, &jsThis_);

    napi_valuetype type;
    napi_typeof(env, value, &type);
    if (type == napi_undefined) {
        return;
    }

    napi_value jsFunc;
    status = napi_get_named_property(env, value, "onLoadStart", &jsFunc);
    if (status == napi_ok) {
        napi_typeof(env, jsFunc, &type);
        if (type != napi_undefined) {
            napi_create_reference(env, jsFunc, 1, &onLoadStart_);
            onLoadStart = [this](ImageInfo imageInfo) {
                this->CallJsOnLoadStart(imageInfo);
            };
        }
    }

    status = napi_get_named_property(env, value, "onLoadSuccess", &jsFunc);
    if (status == napi_ok) {
        napi_typeof(env, jsFunc, &type);
        if (type != napi_undefined) {
            napi_create_reference(env, jsFunc, 1, &onLoadSuccess_);
            onLoadSuccess = [this](ImageInfo imageInfo) {
                this->CallJsOnLoadSuccess(imageInfo);
            };
        }
    }

    status = napi_get_named_property(env, value, "onLoadFailed", &jsFunc);
    if (status == napi_ok) {
        napi_typeof(env, jsFunc, &type);
        if (type != napi_undefined) {
            napi_create_reference(env, jsFunc, 1, &onLoadFailed_);
            onLoadFailed = [this](std::string err, ImageInfo imageInfo) {
                this->CallJsOnLoadFailed(err, imageInfo);
            };
        }
    }

    status = napi_get_named_property(env, value, "onLoadCancel", &jsFunc);
    if (status == napi_ok) {
        napi_typeof(env, jsFunc, &type);
        if (type != napi_undefined) {
            napi_create_reference(env, jsFunc, 1, &onLoadCancel_);
            onLoadCancel = [this](std::string reason, ImageInfo imageInfo) {
                this->CallJsOnLoadCancel(reason, imageInfo);
            };
        }
    }
}

void OnLoadCallBackNapi::CallJsOnLoadStart(ImageInfo &imageInfo)
{
    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadStart_, &jsFunc);
    
    // 获取 request 中的 imageKnifeData 并转换为 JS 对象
    napi_value jsImageKnifeData = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);
    status = napi_call_function(env_, thisValue, jsFunc, 1, &jsImageKnifeData, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "OnLoadCallBack::CallJsOnLoadStart",
                     "call js onLoadStart failed, napi status: %{public}d",
                     status);
    }
}


void OnLoadCallBackNapi::CallJsOnLoadSuccess(ImageInfo &imageInfo)
{
    // 调用 ConvertToJSObject 将 ImageKnifeData 转换为 JS 对象
    napi_value jsImageKnifeData = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);
    
    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;

    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadSuccess_, &jsFunc);

    // 调用 JavaScript 回调函数
    status = napi_call_function(env_, thisValue, jsFunc, 1, &jsImageKnifeData, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "OnLoadCallBack::CallJsOnLoadSuccess",
                     "call js onLoadSuccess failed, napi status: %{public}d",
                     status);
    }
}

void OnLoadCallBackNapi::CallJsOnLoadFailed(std::string err, ImageInfo &imageInfo)
{
    const int argc = 2;
    napi_value args[2];
    napi_create_string_utf8(env_, err.c_str(), NAPI_AUTO_LENGTH, &args[0]);
    args[1] = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadFailed_, &jsFunc);
    
    status = napi_call_function(env_, thisValue, jsFunc, argc, args, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "ImageKnifeNode",
                     "call js onLoadFailed failed, napi status: %{public}d",
                     status);
    }
}

void OnLoadCallBackNapi::CallJsOnLoadCancel(std::string reason, ImageInfo &imageInfo)
{
    const int argc = 2;
    napi_value args[2];
    napi_create_string_utf8(env_, reason.c_str(), NAPI_AUTO_LENGTH, &args[0]);
    args[1] = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadCancel_, &jsFunc);

    status = napi_call_function(env_, thisValue, jsFunc, argc, args, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN,
                     "ImageKnifeNode",
                     "call js onLoadCancel failed, napi status: %{public}d",
                     status);
    }
}

OnLoadCallBackNapi::~OnLoadCallBackNapi()
{
    if (onLoadStart != nullptr) {
        napi_delete_reference(env_, onLoadStart_);
    }

    if (onLoadSuccess != nullptr) {
        napi_delete_reference(env_, onLoadSuccess_);
    }

    if (onLoadFailed != nullptr) {
        napi_delete_reference(env_, onLoadFailed_);
    }

    if (onLoadCancel != nullptr) {
        napi_delete_reference(env_, onLoadCancel_);
    }

    napi_delete_reference(env_, jsThis_);
}

}
